<?php
/**
 * 用Tree树实现敏感词树.
 * User: paul
 * Date: 2018/2/5 0005
 * Time: 11:20
 */

namespace common\helpers;


class FilterWords
{
    private $tree = [];

    /**
     * 插入敏感词
     * @param $utf8Str string 需要添加的敏感词
     */
    public function insert($utf8Str)
    {
        $chars = $this->getChars($utf8Str);
        $chars[] = null;	//串结尾字符
        $count = count($chars);
        $T = &$this->tree;
        for($i = 0; $i < $count; $i++){
            $c = $chars[$i];
            if(!array_key_exists($c, $T)){
                $T[$c] = [];	//插入新字符，关联数组
            }
            $T = &$T[$c];
        }
    }


    /**
     * 从列表中删除敏感词
     * @param $utf8Str string 需要删除的敏感词
     */
    public function remove($utf8Str)
    {
        $chars = &$this->getChars($utf8Str);
        $chars[] = null;
        if($this->_find($chars)){ //先保证此串在树中
            $chars[] = null;
            $count = count($chars);
            $T = &$this->tree;
            for($i = 0; $i < $count; $i++){
                $c = $chars[$i];
                if(count($T[$c]) == 1){ //表明仅有此串
                    unset($T[$c]);
                    return;
                }
                $T = &$T[$c];
            }
        }
    }


    /**
     * 验证是否包含敏感词
     * @param $utf8Str string 需要验证的字符串
     * @param $doCount bool 是否统计敏感词出现的次数
     * @return boolean
     */
    public function contain($utf8Str, $doCount = false)
    {
        $chars = $this->getChars($utf8Str);
        $chars[] = null;
        $len = count($chars);
        $Tree = &$this->tree;
        $count = 0;

        for($i = 0; $i < $len; $i++){
            $c = $chars[$i];
            if(array_key_exists($c, $Tree)){	//起始字符匹配
                $T = &$Tree[$c];
                for($j = $i + 1; $j < $len; $j++){
                    $c = $chars[$j];

                    if(array_key_exists(null, $T)){
                        if($doCount){
                            $count++;
                        } else {
                            return true;
                        }
                    }

                    if(!array_key_exists($c, $T)){
                        break;
                    }

                    $T = &$T[$c];
                }
            }
        }

        if($doCount) {
            return $count;
        } else {
            return false;
        }
    }


    public function contain_all($str_array)
    {
        foreach($str_array as $str){
            if($this->contain($str)){
                return true;
            }
        }
        return false;
    }

    private function _find(&$chars)
    {
        $count = count($chars);
        $T = &$this->tree;
        for($i = 0;$i < $count;$i++){
            $c = $chars[$i];
            if(!array_key_exists($c, $T)){
                return false;
            }
            $T = &$T[$c];
        }
        return true;
    }


    /**
     * 初始化敏感词树
     * @param $str
     */
    public function import($str)
    {
        $this->tree = unserialize($str);
    }

    /**
     * 更新敏感词列表
     */
    public function export()
    {
        return serialize($this->tree);
    }


    /**
     * 分词的方法 将字符串拆分数组
     * @param:$utf8Str string 字符串
     * @return array
     */
    public function getChars($utf8Str)
    {
        $s = $utf8Str;
        $len = strlen($s);
        if($len == 0) return [];
        $chars = [];

        for($i = 0; $i < $len; $i++){
            $c = $s[$i];
            $n = ord($c);

            if(($n >> 7) == 0){		//0xxx xxxx, asci, single
                $chars[] = $c;
            } else if (($n >> 4) == 15){ 	//1111 xxxx, first in four char
                if($i < $len - 3){
                    $chars[] = $c.$s[$i + 1].$s[$i + 2].$s[$i + 3];
                    $i += 3;
                }
            } else if (($n >> 5) == 7){ 	//111x xxxx, first in three char
                if($i < $len - 2){
                    $chars[] = $c.$s[$i + 1].$s[$i + 2];
                    $i += 2;
                }
            } else if (($n >> 6) == 3){ 	//11xx xxxx, first in two char
                if($i < $len - 1){
                    $chars[] = $c.$s[$i + 1];
                    $i++;
                }
            }
        }

        return $chars;
    }
}